﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Collections;

namespace Czf_Serialize
{
	public class CzfStreamReader : IDisposable
	{
        public static bool heightInt = true;

		public event EventException OnError;
		public int zipLv;

		BinaryReader reader;

		public CzfStreamReader (byte[] bytes, int zipLv = 0)
		{
			this.zipLv = zipLv;

			if (this.zipLv == 0) {
				reader = new BinaryReader(new MemoryStream(bytes));
			} else {
				reader = new BinaryReader(new MemoryStream(CzfZipHelper.UnZip (bytes)));
			}
		}

        short ReadInt16L()
        {
            if (heightInt)
                return reader.ReadInt16();
            byte bt1 = reader.ReadByte();
            byte bt2 = reader.ReadByte();
            return (short)(bt1 << 8 | bt2);
        }

        int ReadInt32L()
        {
            if (heightInt)
                return reader.ReadInt32();
            byte bt1 = reader.ReadByte();
            byte bt2 = reader.ReadByte();
            byte bt3 = reader.ReadByte();
            byte bt4 = reader.ReadByte();
            return (int)(bt1 << 24 | bt2 << 16 | bt3 << 8 | bt4);
        }

        uint ReadUInt32L()
        {
            if (heightInt)
                return reader.ReadUInt32();
            byte bt1 = reader.ReadByte();
            byte bt2 = reader.ReadByte();
            byte bt3 = reader.ReadByte();
            byte bt4 = reader.ReadByte();
            return (uint)(bt1 << 24 | bt2 << 16 | bt3 << 8 | bt4);
        }

        long ReadInt64L()
        {
            if (heightInt)
                return reader.ReadInt64();
            byte bt1 = reader.ReadByte();
            byte bt2 = reader.ReadByte();
            byte bt3 = reader.ReadByte();
            byte bt4 = reader.ReadByte();
            byte bt5 = reader.ReadByte();
            byte bt6 = reader.ReadByte();
            byte bt7 = reader.ReadByte();
            byte bt8 = reader.ReadByte();
            return (long)(bt1 << 56 | bt2 << 48 | bt3 << 40 | bt4 << 32 | bt5 << 24 | bt6 << 16 | bt7 << 8 | bt8);
        }

		public Object ReadObject()
		{
			CzfSerializeType czfType = (CzfSerializeType)reader.ReadByte ();
			Object ret;
			switch (czfType) {
			case CzfSerializeType.Byte:
				ret = reader.ReadByte ();
				break;
			case CzfSerializeType.Boolean:
				ret = reader.ReadBoolean ();
				break;
			case CzfSerializeType.Int16:
                ret = ReadInt16L();
				break;
			case CzfSerializeType.Int32:
                ret = ReadInt32L();
				break;
			case CzfSerializeType.UInt32:
                ret = ReadUInt32L();
				break;
			case CzfSerializeType.Int64:
                ret = ReadInt64L();
				break;
			case CzfSerializeType.Float:
				ret = reader.ReadSingle ();
				break;
			//case CzfSerializeType.Double:
				//ret = reader.ReadDouble ();
				//break;
			case CzfSerializeType.String:
				ret = ReadString ();
				break;
			case CzfSerializeType.DateTime:
				ret = new DateTime (reader.ReadInt64 ());
				break;
			case CzfSerializeType.Array:
				ret = ReadArray ();
				break;
			//case CzfSerializeType.GenericList:
				//ret = ReadGenericList ();
				//break;
			//case CzfSerializeType.GenericDictionary:
				//ret = ReadGenericDictionary ();
				//break;
			case CzfSerializeType.Hashtable:
				ret = ReadHashtable ();
				break;
			case CzfSerializeType.Null:
				ret = null;
				break;
			//case CzfSerializeType.Class:
				//ret = ReadClass ();
				//break;
			default:
				Exception e = new Exception ("Read Error!!! czfType=" + czfType);
				if (OnError != null)
					OnError (e);
				throw e;
			}

			return ret;
		}

		public Object ReadClass()
		{
			string assemblyName = ReadString ();
			string typeName = ReadString ();
			int iLen = ReadInt32L ();
			Object obj = Assembly.Load(assemblyName).CreateInstance(typeName);
			if (obj == null) {
				Exception e = new Exception ("undefined typename:" + typeName + "  assemblyName:" + assemblyName);
				if (OnError != null)
					OnError (e);
				throw e;
			}
			for (int i = 0; i < iLen; i++) {
				string FieldName = ReadString ();
				Object value = ReadObject ();
				obj.GetType().GetField (FieldName).SetValue (obj, value);
			}
			return obj;
		}

		public string ReadString()
		{
            int iLen = ReadInt16L ();
			byte[] bytes = reader.ReadBytes (iLen);
			return System.Text.UnicodeEncoding.UTF8.GetString (bytes);
		}

		public object[] ReadArray()
		{
            int iLen = ReadInt32L ();
			object[] array = new object[iLen];
			for(int i=0;i<iLen;i++){
				array [i] = ReadObject ();
			}
			return array;
		}

		public IList ReadGenericList()
		{
			Type valType = typeof(List<>);
			valType = valType.MakeGenericType(CzfGetType());
			IList list = (IList)Activator.CreateInstance(valType);

            int iLen = ReadInt32L ();
			for(int i=0;i<iLen;i++){
				list.Add(ReadObject ());
			}
			return list;
		}

		public IDictionary ReadGenericDictionary()
		{
			Type valType = typeof(Dictionary<,>);
			valType = valType.MakeGenericType(CzfGetType(), CzfGetType());
			IDictionary dict = (IDictionary)Activator.CreateInstance(valType);

            int iLen = ReadInt32L ();
			for(int i=0;i<iLen;i++){
				object key = ReadObject ();
				object value = ReadObject ();
				dict.Add (key, value);
			}
			return dict;
		}

		public Hashtable ReadHashtable()
		{
            int iLen = ReadInt32L ();
			Hashtable dict = new Hashtable (iLen);
			for(int i=0;i<iLen;i++){
				object key = ReadObject ();
				object value = ReadObject ();
				dict.Add (key, value);
			}
			return dict;
		}

		private Type CzfGetType()
		{
			//Type valType;
			CzfSerializeType czfType = (CzfSerializeType)reader.ReadByte ();
			switch (czfType) {
			case CzfSerializeType.Byte:
				return typeof(byte);
			case CzfSerializeType.Boolean:
				return typeof(bool);
			case CzfSerializeType.Int16:
				return typeof(short);
			case CzfSerializeType.Int32:
				return typeof(int);
			case CzfSerializeType.Int64:
				return typeof(long);
			case CzfSerializeType.Float:
				return typeof(float);
			//case CzfSerializeType.Double:
				//return typeof(double);
			case CzfSerializeType.String:
				return typeof(string);
			case CzfSerializeType.Array:
				return typeof(object[]);
			//case CzfSerializeType.GenericList:
			//	valType = typeof(List<>);
			//	valType = valType.MakeGenericType(CzfGetType());
			//	return valType;
			//case CzfSerializeType.GenericDictionary:
				//valType = typeof(Dictionary<,>);
				//valType = valType.MakeGenericType (CzfGetType (), CzfGetType ());
				//return valType;
			case CzfSerializeType.Hashtable:
				return typeof(Hashtable);
			case CzfSerializeType.Null:
				return typeof(object);
			//case CzfSerializeType.Class:
				//string assemblyName = ReadString ();
				//string typeName = ReadString ();
				//return Assembly.Load (assemblyName).GetType (typeName);
			default:
				Exception e = new Exception ("Read Error!!! czfType=" + czfType);
				if (OnError != null)
					OnError (e);
				throw e;
			}

		}

		#region IDisposable implementation

		public void Dispose ()
		{
			reader.Close ();
		}

		#endregion
	}
}

